// vmunit.hpp
//
// Copyright (C) 2008  Maksim Sipos <msipos@mailc.net>
//
// This file is a part of the Ripe language project.
//
// Ripe is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#ifndef R_VMUNIT_HPP
#define R_VMUNIT_HPP

#include "util/standard.hpp"

// "Unit" is the most basic element of a VM binary. It is a 32-bit value
// that can represent an instruction, or an argument to an instruction:
// anything from an address in the segment, symbol table index, register...
//
// We call the leftmost 4 bits of a Unit its type. The remaining 28 bits
// are termed the "body".

namespace Ripe {
  typedef uint32_t Unit;

  // Masks that separate the type and the body.
  #define UNIT_TYPE_MASK     ((uint32_t) 0xF0000000)
  #define UNIT_BODY_MASK     ((uint32_t) 0x0FFFFFFF)
  #define UNIT_OPERANDS_MASK ((uint32_t) 0x0FF00000)

  // This Unit is an instruction (body defines which).
  #define UNIT_INSTRUCTION   ((uint32_t) 0x00000000)

  // This Unit is an address (body defines where in the segment).
  #define UNIT_ADDRESS       ((uint32_t) 0x10000000)

  // A direct value (body defines the index in the direct value table).
  #define UNIT_DIRECT_VALUE  ((uint32_t) 0x20000000)

  // A symbol (body is the index in the symbol table).
  #define UNIT_SYMBOL        ((uint32_t) 0x30000000)

  // A local symbol.
  #define UNIT_LOCAL_SYMBOL  ((uint32_t) 0x40000000)

  // A global symbol.
  #define UNIT_GLOBAL_SYMBOL ((uint32_t) 0x50000000)

  // A register specifier (body defines index of the register).
  #define UNIT_REGISTER      ((uint32_t) 0x60000000)

  // Return the type of the RUnit (one of R_UNIT_* constants).
  #define unit_type(unit)    ((unit) & UNIT_TYPE_MASK)

  // Return the body of the RUnit.
  #define unit_body(unit)    ((unit) & UNIT_BODY_MASK)

  // Return the number of operands in an instruction RUnit.
  #define unit_operands(unit) ((unit & UNIT_OPERANDS_MASK) >> 20)

  // Clean RUnit constructors.
  #define unit_new(type, body)  ((Unit) ( \
                                    (((uint32_t) body) & UNIT_BODY_MASK) \
                                    | type))
  #define unit_address(n)       unit_new(UNIT_ADDRESS, n)
  #define unit_direct_value(n)  unit_new(UNIT_DIRECT_VALUE, n)
  #define unit_symbol(n)        unit_new(UNIT_SYMBOL, n)
  #define unit_local_symbol(n)  unit_new(UNIT_LOCAL_SYMBOL, n)
  #define unit_global_symbol(n) unit_new(UNIT_GLOBAL_SYMBOL, n)
  #define unit_register(n)      unit_new(UNIT_REGISTER, n)

  // RUnit instruction constructor
  #define unit_instruction(n,args)   unit_new(UNIT_INSTRUCTION, n | \
                                      (args << 20))

  // This instruction assigns the value of operand2 to operand1.
  #define VM_ASSIGN     1

  // This instruction calls the function given in operand1, passing
  // remaining operands as arguments. Any return value from the function
  // call is ignored.
  #define VM_CALL       2
  
  // This instruction calls the function given in operand1, puts the
  // return value of the call into operand2 (which must be a symbol), and
  // passes any remaining operands as arguments.
  #define VM_CALLR      3
  
  // This instruction accepts a function call. It assigns the operands that
  // were a part of the call instruction into the operands of this
  // instruction. For example:
  //           CALL &func, 1, 2, 3
  //    func:  ACCEPT a, b, c
  //           # At this point in the function a = 1, b = 2, c = 3.
  #define VM_ACCEPT     4
  
  // Make an unconditional jump to the location given in operand1.
  #define VM_JUMP       5
  
  // Jump to location given in operand1 if operand2 == true.
  #define VM_JUMPIF     6
  
  // Return from a call. An optional operand1 will be the return value of
  // the function.
  #define VM_RET        7
}

#endif // R_VMUNIT_HPP
